﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Djs.Neuronic.Medium;

namespace Djs.Neuronic.Brain
{
    #region Stator : Statický přístupový bod k singletonu CreatorCls
    /// <summary>
    /// Stator : Statický přístupový bod k singletonu CreatorCls
    /// </summary>
    public class Stator
    {
        #region Konstrukce
        internal static void SetInstance(CreatorCls creator)
        {
            if (creator == null)
            {
                _Creator = null;
            }
            else
            {
                string stack = (new System.Diagnostics.StackTrace(true)).ToString();
                if (_Creator == null)
                {
                    _CreatorStack = stack;
                    _Creator = creator;
                }
                else
                {
                    if (!Object.ReferenceEquals(_Creator, creator))
                    {
                        string eol = Environment.NewLine;
                        System.Windows.Forms.MessageBox.Show("Duplicitní iniciace singletonu Creator." + eol + "První iniciace:" + eol + _CreatorStack + eol + "Duplicitní pokus:" + eol + stack, "Chyba", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
                    }
                }
            }
        }
        private static CreatorCls _Creator;
        private static string _CreatorStack;
        #endregion
        #region Přístup na vlastnosti Creatora
        public static DeclarationsCls Declarations { get { return _Creator.Declarations; } }
        /// <summary>
        /// Nastavení
        /// </summary>
        public static SettingCls Setting { get { return _Creator.Setting; } }
        /// <summary>
        /// Přístup k databázi
        /// </summary>
        public static DatabaseCls Database { get { return _Creator.Database; } }
        /// <summary>
        /// Jazykové znalosti.
        /// Interně obsahují vícero jazyků.
        /// Součástí je kompletní knihovna frází a vět, a gramatické znalosti.
        /// </summary>
        public static SpeechCls Speech { get { return _Creator.Speech; } }
        public static MemoriesCls Memories { get { return _Creator.Memories; } }
        public static UnitCls Unit { get { return _Creator.Unit; } }
        public static MoodCls Mood { get { return _Creator.Mood; } }
        public static IndividualityCls Individuality { get { return _Creator.Individuality; } }

        // Zkratky na další objekty:
        /// <summary>
        /// Deklarace objektů databáze načtená z XML dokumentu
        /// </summary>
        public DatabaseDeclarationCls DatabaseDeclaration { get { return _Creator.Declarations.DatabaseDeclaration; } }
        /// <summary>
        /// Deklarace datových tříd načtená z XML dokumentu
        /// </summary>
        public ClassDeclarationCls ClassDeclaration { get { return _Creator.Declarations.ClassDeclaration; } }

        #endregion
    }
    #endregion
    #region CreatorCls : Creator (Tvůrce) : jeho úkolem je vytvořit instanci sebe sama, a do sebe vytvořit instance každého jednoho potřebného specialisty.
    /// <summary>
    /// Creator (Tvůrce) : jeho úkolem je vytvořit instanci sebe sama, a do sebe vytvořit instance každého jednoho potřebného specialisty.
    /// Instance vytváří a inicializuje ve správném pořadí.
    /// </summary>
    public partial class CreatorCls : IDisposable
    {
        #region Konstrukce a Dispose, Components Prepare, SelfTest, Dispose
        private CreatorCls()
        {
            this.LogItems = new List<LogItem>();
            this.ServicePrepare();
        }
        public static CreatorCls Create()
        {
            CreatorCls creator = new CreatorCls();

            Stator.SetInstance(creator);            // Duplicita
            creator.CreateComponents();
            creator.PrepareComponents();
            creator.SelfTestComponents();

            return creator;
        }
        void IDisposable.Dispose()
        {
            this.DisposeComponents();
        }
        private void CreateComponents()
        {
            this.Components = new List<CommonCls>();

            this.Declarations = DeclarationsCls.Create(this); this.Components.Add(this.Declarations);
            this.Setting = SettingCls.Create(this); this.Components.Add(this.Setting);
            this.Database = DatabaseCls.Create(this); this.Components.Add(this.Database);
            this.Speech = SpeechCls.Create(this); this.Components.Add(this.Speech);
            this.Memories = MemoriesCls.Create(this); this.Components.Add(this.Memories);
            this.Unit = UnitCls.Create(this); this.Components.Add(this.Unit);
            this.Mood = MoodCls.Create(this); this.Components.Add(this.Mood);
            this.Individuality = IndividualityCls.Create(this); this.Components.Add(this.Individuality);
        }
        private void PrepareComponents()
        {
            this.Components.ForEach(c => c.Prepare());
        }
        private void SelfTestComponents()
        {
            this.Components.ForEach(c => c.SelfTest());
        }
        private void DisposeComponents()
        {
            this.Components.ForEach(c => c.Dispose());

            this.Declarations = null;
            this.Setting = null;
            this.Database = null;
            this.Speech = null;
            this.Memories = null;
            this.Unit = null;
            this.Mood = null;
            this.Individuality = null;

            this.Components = null;
        }
        protected List<CommonCls> Components;
        public bool IsCompleted { get; set; }
        #endregion
        #region Služby
        /// <summary>
        /// Adresář, kde bydlí aplikace
        /// </summary>
        public string AppPath { get { return this._AppPath; } }
        /// <summary>
        /// Název aplikace, bez adresáře, bez přípony
        /// </summary>
        public string AppName { get { return this._AppName; } }
        /// <summary>
        /// Plný název souboru aplikace (s cestou i příponou)
        /// </summary>
        public string AppFile { get { return this._AppFile; } }
        /// <summary>
        /// Najde a vrátí plnou cestu k danmu souboru.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public string SearchDataFile(string fileName)
        {
            string fullName = null;
            this._SearchDataFile(fileName, ref fullName, this._AppPath, "", true);
            this._SearchDataFile(fileName, ref fullName, this._AppPath, @"..\Neuronic\Data", true);
            this._SearchDataFile(fileName, ref fullName, this._AppPath, @"..\Neuronic", true);
            this._SearchDataFile(fileName, ref fullName, this._AppPath, @"..", true);
            return fullName;
        }
        /// <summary>
        /// Podpora pro hledání souboru
        /// </summary>
        /// <param name="fileName">Hledaný název (bez cesty, s příponou)</param>
        /// <param name="fullName">In/Out nalezený soubor. Pokud je na vstupu vyplněn, pak tato metoda už nic nehledá.</param>
        /// <param name="directory">Adresář, kde se má hledat</param>
        /// <param name="withSubDir">Příznak, zda hledat včetně podadresářů</param>
        private void _SearchDataFile(string fileName, ref string fullName, string directory, string relativeDirectory, bool withSubDir)
        {
            if (fullName != null) return;
            string dir = directory;
            if (!String.IsNullOrEmpty(relativeDirectory))
                dir = System.IO.Path.Combine(dir, relativeDirectory);
            System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(dir);
            if (dirInfo.Exists)
            {
                System.IO.SearchOption option = (withSubDir ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly);
                System.IO.FileInfo[] files = dirInfo.GetFiles(fileName, option);
                if (files.Length > 0)
                    fullName = files[0].FullName;
            }
        }
        private void ServicePrepare()
        {
            this._PrepareAppPaths();
            Stator.SetInstance(this);
        }
        private void _PrepareAppPaths()
        {
            this._AppFile = System.Windows.Forms.Application.ExecutablePath;
            this._AppPath = System.IO.Path.GetDirectoryName(this._AppFile);
            this._AppName = System.IO.Path.GetFileNameWithoutExtension(this._AppFile);
        }
        /// <summary>
        /// Plný název souboru aplikace (s cestou i příponou)
        /// </summary>
        private string _AppFile;
        /// <summary>
        /// Adresář aplikace
        /// </summary>
        private string _AppPath;
        /// <summary>
        /// Název aplikace, bez adresáře, bez přípony
        /// </summary>
        private string _AppName;
        #endregion
        #region Property = jednotliví specialisté
        public DeclarationsCls Declarations { get; protected set; }
        public SettingCls Setting { get; protected set; }
        public DatabaseCls Database { get; protected set; }
        public SpeechCls Speech { get; protected set; }
        public MemoriesCls Memories { get; protected set; }
        public UnitCls Unit { get; protected set; }
        public MoodCls Mood { get; protected set; }
        public IndividualityCls Individuality { get; protected set; }
        // Zkratky na další objekty:
        /// <summary>
        /// Deklarace objektů databáze načtená z XML dokumentu
        /// </summary>
        public DatabaseDeclarationCls DatabaseDeclaration { get { return this.Declarations.DatabaseDeclaration; } }
        /// <summary>
        /// Deklarace datových tříd načtená z XML dokumentu
        /// </summary>
        public ClassDeclarationCls ClassDeclaration { get { return this.Declarations.ClassDeclaration; } }
        #endregion
        #region Logger
        public void LogAdd(LogItem logItem)
        {
            this.LogItems.Add(logItem);
        }
        public void LogError(string message)
        {
            this.LogItems.Add(new LogItem(LogItemType.Error, message));
        }
        public void LogError(Exception exc, string message)
        {
            this.LogItems.Add(new LogItem(LogItemType.Error, message, exc));
        }
        public void LogFailure(Exception exc, string message)
        {
            this.LogItems.Add(new LogItem(LogItemType.Failure, message, exc));
        }
        private List<LogItem> LogItems;
        public class LogItem
        {
            public LogItem(LogItemType itemType, string message)
            {
                this.ItemType = itemType;
                this.Message = message;
            }
            public LogItem(LogItemType itemType, string message, string source)
            {
                this.ItemType = itemType;
                this.Message = message;
                this.Source = source;
            }
            public LogItem(LogItemType itemType, string message, string source, string stack)
            {
                this.ItemType = itemType;
                this.Message = message;
                this.Source = source;
                this.Stack = stack;
            }
            public LogItem(LogItemType itemType, string message, Exception exc)
            {
                this.ItemType = itemType;
                this.Message = message;
                this.Stack = exc.StackTrace;
            }
            public LogItem(LogItemType itemType, string message, string source, Exception exc)
            {
                this.ItemType = itemType;
                this.Message = message;
                this.Source = source;
                this.Stack = exc.StackTrace;
            }
            public LogItemType ItemType { get; private set; }
            public string Message { get; private set; }
            public string Source { get; private set; }
            public string Stack { get; private set; }
            public bool Reported { get; set; }
            public bool Writed { get; set; }
        }
        /// <summary>
        /// Závažnost informace
        /// </summary>
        public enum LogItemType
        {
            None,
            /// <summary>
            /// Informace o poklidném chodu
            /// </summary>
            Info,
            /// <summary>
            /// Varování před případným problémem
            /// </summary>
            Warning,
            /// <summary>
            /// Chyba, překonatelná za cenu snížení funkčnosti
            /// </summary>
            Error,
            /// <summary>
            /// Selhání systému
            /// </summary>
            Failure
        }
        #endregion
        #region Timer
        public StopWatch GetTimer()
        {
            return new StopWatch();
        }
        public class StopWatch : IDisposable
        {
            public StopWatch()
            {
                this._StopWatch = new System.Diagnostics.Stopwatch();
                this._FrequencySec = (decimal)System.Diagnostics.Stopwatch.Frequency;
                this._FrequencyMiliSec = (decimal)System.Diagnostics.Stopwatch.Frequency / 1000m;
                this._StopWatch.Start();
            }
            private System.Diagnostics.Stopwatch _StopWatch;
            private decimal _FrequencySec;
            private decimal _FrequencyMiliSec;
            void IDisposable.Dispose()
            {
                this._StopWatch.Stop();
                this._StopWatch = null;
            }
            /// <summary>
            /// Dosažený čas v sekundách
            /// </summary>
            public decimal ElapsedSeconds
            {
                get { return (decimal)this._StopWatch.ElapsedTicks / this._FrequencySec; }
            }
            /// <summary>
            /// Dosažený čas v milisekundách
            /// </summary>
            public decimal ElapsedMiliSeconds
            {
                get { return (decimal)this._StopWatch.ElapsedTicks / this._FrequencyMiliSec; }
            }
            public void Reset()
            {
                this._StopWatch.Restart();
            }
        }
        #endregion
        #region CodeReflector
        /// <summary>
        /// Najde a vrátí typy vyhovující dané podmínce
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public List<Type> FindTypes(Func<Type, bool> filter)
        {
            return this._AssemblyTypes.Where(filter).ToList();
        }
        /// <summary>
        /// Načte a vrátí všechny Type ze všech Assembly z this._Assemblies
        /// </summary>
        private List<System.Type> _AssemblyTypes
        {
            get
            {
                List<System.Type> types = new List<Type>();
                foreach (Assembly assembly in this._Assemblies)
                    types.AddRange(assembly.GetTypes());
                return types;
            }
        }
        /// <summary>
        /// Obsahuje naloadované assembly z adresáře aplikace + podadresářů
        /// </summary>
        private List<Assembly> _Assemblies
        {
            get
            {
                if (this.__Assemblies == null)
                {
                    System.IO.DirectoryInfo appPathInfo = new System.IO.DirectoryInfo(this.AppPath);
                    System.IO.FileInfo[] files = appPathInfo.GetFiles("*.*", System.IO.SearchOption.AllDirectories);
                    List<System.IO.FileInfo> fileList = files.Where(fi => _IsProjectFile(fi)).ToList();

                    List<Assembly> assemblies = new List<Assembly>();
                    foreach (System.IO.FileInfo fileInfo in fileList)
                    {
                        try
                        {
                            Assembly assembly = Assembly.LoadFrom(fileInfo.FullName);
                            assemblies.Add(assembly);
                        }
                        catch (Exception exc)
                        { }
                    }
                    this.__Assemblies = assemblies;
                }
                return this.__Assemblies;
            }
        }
        /// <summary>
        /// Vrátí true pro soubory, které je možno loadovat jako assembly
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        private bool _IsProjectFile(System.IO.FileInfo fileInfo)
        {
            string fileName = fileInfo.Name.ToLower();
            string fileExt = fileInfo.Extension.ToLower();
            if (fileName.Contains(".vshost.")) return false;
            if (!(fileExt == ".exe" || fileExt == ".dll")) return false;
            return true;
        }
        private List<Assembly> __Assemblies;
        #endregion
    }
    #endregion
    #region CommonCls : obecný předek všech specialistů. Deklaruje virtual háčky, a přestupní body na Creator a skrze něj na všechny sousední specialisty.
    /// <summary>
    /// CommonCls : obecný předek všech specialistů. Deklaruje virtual háčky, a přestupní body na Creator a skrze něj na všechny sousední specialisty.
    /// </summary>
    public partial class CommonCls : IDisposable
    {
        protected CommonCls(CreatorCls creator)
        {
            this.Creator = creator;
            this.Init();
        }
        void IDisposable.Dispose()
        {
            this.Dispose();
        }
        /// <summary>
        /// Init : proběhne ihned v rámci konstruktoru
        /// </summary>
        protected virtual void Init()
        { }
        /// <summary>
        /// Prepare : proběhne poté, kdy jsou vytvořeny a iniciovány všechny objekty, před SelfTestem.
        /// </summary>
        internal virtual void Prepare()
        { }
        /// <summary>
        /// SelfTest : proběhne na konci procesu vytváření objektu CreatorCls (po Initu, po Prepare, před předáním do používání.
        /// </summary>
        internal virtual void SelfTest()
        { }
        /// <summary>
        /// 
        /// </summary>
        internal virtual void Dispose()
        { }
        /// <summary>
        /// Odkaz na objekt Tvůrce, který je tichým pojítkem mezi všemi komponentami
        /// </summary>
        public CreatorCls Creator { get; private set; }
        /// <summary>
        /// Veškeré deklarace (databáze, třídy) + reflexe kódu, typicky read-only
        /// </summary>
        public DeclarationsCls Declarations { get { return this.Creator.Declarations; } }
        /// <summary>
        /// Přístup k fyzické konfiguraci, s možností Save
        /// </summary>
        public SettingCls Setting { get { return this.Creator.Setting; } }
        /// <summary>
        /// Přístup k informacím uloženým v databázi
        /// </summary>
        public DatabaseCls Database { get { return this.Creator.Database; } }
        /// <summary>
        /// Jazykové znalosti (mluva), může obsahovat vícero jazyků (LanguageCls)
        /// </summary>
        public SpeechCls Speech { get { return this.Creator.Speech; } }
        /// <summary>
        /// Paměť
        /// </summary>
        public MemoriesCls Memories { get { return this.Creator.Memories; } }
        /// <summary>
        /// 
        /// </summary>
        public UnitCls Unit { get { return this.Creator.Unit; } }
        /// <summary>
        /// Současná nálada, úkoly
        /// </summary>
        public MoodCls Mood { get { return this.Creator.Mood; } }
        /// <summary>
        /// Trvalá osobní data
        /// </summary>
        public IndividualityCls Individuality { get { return this.Creator.Individuality; } }
    }
    #endregion
    #region Základní deklarace tříd jednotlivých specialistů. Zde je pouze jejich konstrukce, kterou vyžaduje Creator.
    #region class DeclarationsCls
    public partial class DeclarationsCls : CommonCls
    {
        #region Konstrukce
        protected DeclarationsCls(CreatorCls creator) : base(creator) { }
        internal static DeclarationsCls Create(CreatorCls creator) { return new DeclarationsCls(creator); }
        #endregion
    }
    #endregion
    #region class SettingCls
    public partial class SettingCls : CommonCls
    {
        #region Konstrukce
        protected SettingCls(CreatorCls creator) : base(creator) { }
        internal static SettingCls Create(CreatorCls creator) { return new SettingCls(creator); }
        #endregion
    }
    #endregion
    #region class DatabaseCls
    public partial class DatabaseCls : CommonCls
    {
        #region Konstrukce
        protected DatabaseCls(CreatorCls creator) : base(creator) { }
        internal static DatabaseCls Create(CreatorCls creator) { return new DatabaseCls(creator); }
        #endregion
    }
    #endregion
    #region class SpeechCls
    public partial class SpeechCls : CommonCls
    {
        #region Konstrukce
        protected SpeechCls(CreatorCls creator) : base(creator) { }
        internal static SpeechCls Create(CreatorCls creator)
        {
            return new SpeechCls(creator);
        }
        #endregion
    }
    #endregion
    #region class MemoriesCls
    public partial class MemoriesCls : CommonCls
    {
        #region Konstrukce
        protected MemoriesCls(CreatorCls creator) : base(creator) { }
        internal static MemoriesCls Create(CreatorCls creator)
        {
            return new MemoriesCls(creator);
        }
        #endregion
    }
    #endregion
    #region class UnitCls
    public partial class UnitCls : CommonCls
    {
        #region Konstrukce
        protected UnitCls(CreatorCls creator) : base(creator) { }
        internal static UnitCls Create(CreatorCls creator) { return new UnitCls(creator); }
        #endregion
    }
    #endregion
    #region class MoodCls
    public partial class MoodCls : CommonCls
    {
        #region Konstrukce
        protected MoodCls(CreatorCls creator) : base(creator) { }
        internal static MoodCls Create(CreatorCls creator)
        {
            return new MoodCls(creator);
        }
        #endregion
    }
    #endregion
    #region class IndividualityCls
    public partial class IndividualityCls : CommonCls
    {
        #region Konstrukce
        protected IndividualityCls(CreatorCls creator) : base(creator) { }
        internal static IndividualityCls Create(CreatorCls creator)
        {
            return new IndividualityCls(creator);
        }
        #endregion
    }
    #endregion
    #endregion
}
